package com.wzw.basics.concurrent.lock.spin;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁，是指尝试获取锁的线程不会立即阻塞，而是采用循环的方式去尝试获取锁，这样的好处是减少线程上下文切换的消耗，缺点是循环会消耗CPU
 * @author Wangzhiwen
 */
public class SpinLock {

    // 使用原子引用类，包装thread或者runnable
    private static AtomicReference<Thread> lock = new AtomicReference<>();

    // cas加锁
    public static void lock(){
        // 获取当前线程
        Thread currentThread = Thread.currentThread();

        System.out.println(currentThread.getName() + " come in");

//        while (!lock.compareAndSet(null,currentThread)){
//
//        }
        // 自旋，期望是没有线程占有。比较成功才会退出循环，否则线程就一直循环比较
        for (;;){
            if (lock.compareAndSet(null,currentThread)) break;
        }
    }

    // 解锁
    public static void unlock(){
        Thread thread = Thread.currentThread();
        // 期望是当前线程，如果是就设置为null。这里如果循环的话，会造成线程解锁也会一直阻塞住。cas不成功也没影响
        lock.compareAndSet(thread,null);

        System.out.println(thread.getName() + " unlock");
    }

    public static void main(String[] args) {
        Resource resource = new Resource(1, "xxx");

        new Thread(() -> {

            lock();

            resource.setName(Thread.currentThread().getName());

            System.out.println(resource);

            unlock();

        }, "AAA").start();

        new Thread(() -> {

            lock();

            resource.setName(Thread.currentThread().getName());

            System.out.println(resource);

            unlock();

        }, "BBB").start();
    }
}

class Resource {
    private int number;
    private String name;

    public Resource(int number, String name) {
        this.number = number;
        this.name = name;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Resource{" +
                "number=" + number +
                ", name='" + name + '\'' +
                '}';
    }
}
